#ifndef __REDCELL_DATA_HPP__
#define __REDCELL_DATA_HPP__

#include <string>
#include <sstream>
#include <map>
#include <cassert>
#include "common.hpp"

#ifdef REDCELL_WITHBOOST
#include <boost/pool/pool_alloc.hpp>
#endif

namespace redcell {


  inline String typeToString(const char type) {
    switch (type) {
    case NIL: return "NIL";
    case INTEGER: return "INTEGER";
    case REAL: return "REAL";
    case STRING: return "STRING";
    case TABLE: return "TABLE";

    case INTKEY: return "INTKEY";
    case STRKEY: return "STRKEY";
    }
  }


  struct TypeError: public Error {
    static String formatWhat(const char wrong, const char right) {
      std::ostringstream os;
      os << "Type: Attempting to get " 
	 << typeToString(wrong) 
	 << " from " << typeToString(right);
      return os.str();
    }

    TypeError(const char wrong, const char right): Error(formatWhat(wrong, right)) {}
  };


  // Key

#if REDCELL_STRINGKEYS

  struct Key {
    char keyType;

    union {
      IntKey intKey;
      char strKeyBuffer[sizeof(StrKey)];
    };

    char type() const { return keyType; }

    StrKey &strKey() { return *reinterpret_cast<StrKey *>(strKeyBuffer); }
    const StrKey &strKey() const { return *reinterpret_cast<const StrKey *>(strKeyBuffer); }


    Key(const Key &rhs) {
      keyType = rhs.keyType;
      switch (keyType) {
      case INTKEY: intKey = rhs.intKey; break;
      case STRKEY: new(&strKey()) StrKey(rhs.strKey()); break;
      default: assert(false); break;
      }
    }

    Key(const IntKey rhs) {
      keyType = INTKEY;
      intKey = rhs;
    }

    Key(const StrKey rhs) {
      keyType = STRKEY;
      new(&strKey()) StrKey(rhs);
    }

    ~Key() {
      if (keyType == STRKEY) {
	strKey().~StrKey();
      }
    }

    void checkType(const char type) const {
      if (type != keyType) {
	throw TypeError(type, keyType);
      }
    }


    IntKey ik() const {
      checkType(INTKEY);
      return intKey;
    }

    StrKey sk() const {
      checkType(STRKEY);
      return strKey();
    }
  };

  struct KeyLess {
    bool operator ()(const Key &k1, const Key &k2) {
      if (k1.keyType < k2.keyType) {
	return true;
      }
      if (k1.keyType > k2.keyType) {
	return false;
      }
      switch (k1.keyType) {
      case INTKEY: return k1.intKey < k2.intKey; break;
      case STRKEY: return k1.strKey() < k2.strKey(); break;
      default: assert(false); break;
      }
    }
  };

  inline std::ostream &operator <<(std::ostream &os, const Key key) {
    switch (key.keyType) {
    case INTKEY: os << key.intKey; break;
    case STRKEY: os << key.strKey(); break;
    default: assert(false); break;
    }
    return os;
  }

#else

  typedef std::less<Key> KeyLess;

#endif




  struct KeyError: public Error {
    static String formatWhat(const Key key) {
      std::ostringstream os;
      os << "Key: " << key << " not exist";
      return os.str();
    }

    KeyError(const Key key): Error(formatWhat(key)) {}
  };




  // TableImpl

  struct Variant;



#if REDCELL_WITHBOOST
    typedef std::map<Key, Variant, KeyLess, 
                     boost::fast_pool_allocator<std::pair<const Key, Variant> > > TableMap;
#else
  typedef std::map<Key, Variant, KeyLess> TableMap;
#endif


  struct TableImpl: public TableMap {
    volatile size_t cnt;

    TableImpl() {
      cnt = 0;
    }
      
    void incRef() {
      ++cnt;
    }

    void decRef() {
      if (!--cnt) {
	delete this;
      }
    }
  };


  // Table

  struct Table {
    TableImpl *impl;

    typedef TableImpl::iterator iterator;
    typedef TableImpl::const_iterator const_iterator;
    typedef TableImpl::reverse_iterator reverse_iterator;
    typedef TableImpl::const_reverse_iterator const_reverse_iterator;

    Table() {
      impl = new TableImpl;
      impl->incRef();
    }

    Table(const Table &rhs) {
      impl = rhs.impl;
      impl->incRef();
    }

    ~Table() {
      impl->decRef();
    }

    Table &operator =(const Table &rhs) {
      impl->decRef();
      impl = rhs.impl;
      impl->incRef();
    }

    TableImpl *operator ->() const {
      return impl;
    }

    template <typename AnyKey> 
    void del(const AnyKey key) {
      impl->erase(Key(key)); 
    }

    // forwards begin

    template<typename AnyKey> const Variant &findItem(const AnyKey key) const;
    template<typename AnyKey> Variant &ensureItem(const AnyKey key);

    template<typename AnyKey> Integer &i(const AnyKey key);
    template<typename AnyKey> Real &r(const AnyKey key);
    template<typename AnyKey> String &s(const AnyKey key);
    template<typename AnyKey> Table &t(const AnyKey key);

    template<typename AnyKey> const Integer &ci(const AnyKey key) const;
    template<typename AnyKey> const Real &cr(const AnyKey key) const;
    template<typename AnyKey> const String &cs(const AnyKey key) const;
    template<typename AnyKey> const Table &ct(const AnyKey key) const;

    template<typename AnyKey> void nil(const AnyKey key);

    template<typename AnyKey> char type(const AnyKey key) const;

    void merge(const Table &patch);

    // forwards end

    template<typename AnyKey> const Integer &i(const AnyKey key) const { return ci(key); }
    template<typename AnyKey> const Real &r(const AnyKey key) const { return cr(key); }
    template<typename AnyKey> const String &s(const AnyKey key) const { return cs(key); }
    template<typename AnyKey> const Table &t(const AnyKey key) const { return ct(key); }

    template<typename AnyKey> Table &operator ()(const AnyKey key) { return t(key); }
    template<typename AnyKey> const Table &operator ()(const AnyKey key) const { return ct(key); }
  };


  // Variant


  struct Variant {
    char varType;

#define UNION_MEMBERS					\
    Integer integer;					\
    Real real;						\
    char stringBuffer[sizeof(String)];			\
    char tableBuffer[sizeof(Table)];

    union {
      UNION_MEMBERS
      union {
        UNION_MEMBERS
      } all;
    };

    String &string() { return *reinterpret_cast<String *>(stringBuffer); }
    Table &table() { return *reinterpret_cast<Table *>(tableBuffer); }

    const String &string() const { return *reinterpret_cast<const String *>(stringBuffer); }
    const Table &table() const { return *reinterpret_cast<const Table *>(tableBuffer); }

    void freeRef() {
      switch (varType) {
      case STRING: string().~String(); break;
      case TABLE: table().~Table(); break;
      }
    }

    void checkType(const char type) const {
      if (type != varType) {
	throw TypeError(type, varType);
      }
    }

    void ensureType(const char type) {
      if (type == varType) {
	return;
      }
      freeRef();
      switch (this->varType = type) {
      case STRING: new(&string()) String; break;
      case TABLE: new(&table()) Table; break;
      }
    }


    Integer &i() { ensureType(INTEGER); return integer; }
    Real &r() { ensureType(REAL); return real; }
    String &s() { ensureType(STRING); return string(); }
    Table &t() { ensureType(TABLE); return table(); }

    Table &operator ()() { return t(); }

    const Integer &ci() const { checkType(INTEGER); return integer; }
    const Real &cr() const { checkType(REAL); return real; }
    const String &cs() const { checkType(STRING); return string(); }
    const Table &ct() const { checkType(TABLE); return table(); }

    const Integer &i() const { return ci(); }
    const Real &r() const { return cr(); }
    const String &s() const { return cs(); }
    const Table &t() const { return ct(); }

    const Table &operator ()() const { return ct(); }

    void nil() {
      freeRef();
      varType = NIL;
    }

    char type() const {
      return varType;
    }

    void setVariant(const Variant &rhs) {
      switch (varType = rhs.varType) {
      case STRING: new(&string()) String(rhs.string()); break;
      case TABLE: new(&table()) Table(rhs.table()); break;
      default: all = rhs.all; break;
      }
    }

    Variant() {
      varType = NIL;
    }

    Variant(const Variant &rhs) {
      setVariant(rhs); 
    }

    ~Variant() {
      freeRef();
    }

    Variant &operator =(const Variant &rhs) {
      if (&rhs != this) {
        freeRef();
        setVariant(rhs);
      }
      return *this;
    }
  };


  // methods of Table

  template <typename AnyKey>
  inline const Variant &Table::findItem(const AnyKey key) const {
    TableImpl::iterator it = impl->find(Key(key));
    if (it == impl->end()) {
      throw IntKeyError(key);
    }
    return it->second;
  }

  template <typename AnyKey> 
  inline Variant &Table::ensureItem(const AnyKey key) {
    return (*impl)[Key(key)];
  }

  template <typename AnyKey> inline Integer &Table::i(const AnyKey key) { return ensureItem(key).i(); }
  template <typename AnyKey> inline Real &Table::r(const AnyKey key) { return ensureItem(key).r(); }
  template <typename AnyKey> inline String &Table::s(const AnyKey key) { return ensureItem(key).s(); }
  template <typename AnyKey> inline Table &Table::t(const AnyKey key) { return ensureItem(key).t(); }

  template <typename AnyKey> inline const Integer &Table::ci(const AnyKey key) const { return findItem(key).ci(); }
  template <typename AnyKey> inline const Real &Table::cr(const AnyKey key) const { return findItem(key).cr(); }
  template <typename AnyKey> inline const String &Table::cs(const AnyKey key) const { return findItem(key).cs(); }
  template <typename AnyKey> inline const Table &Table::ct(const AnyKey key) const { return findItem(key).ct(); }
  
  template <typename AnyKey> inline void Table::nil(const AnyKey key) { ensureItem(key).nil(); }

  template <typename AnyKey> inline char Table::type(const AnyKey key) const { return findItem(key).type(); }

  inline void Table::merge(const Table &patch) {
    for (TableImpl::const_iterator i=patch->begin(); i!=patch->end(); ++i) {
      if (i->second.type() == TABLE) {
	ensureItem(i->first).t().merge(i->second.table());
      } else {
	ensureItem(i->first) = i->second;
      }
    }
  }

}

#endif
